package com.pkx.guide.timing;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.pkx.guide.info.entity.TInfoGuidePublish;
import com.pkx.guide.info.entity.TInfoNetworkConfig;
import com.pkx.guide.info.service.TInfoNetworkConfigService;
import com.pkx.guide.qianfeng.service.QianFengPlayLedService;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

@Component
public class ProtocolClient {
    private final TInfoNetworkConfigService infoNetworkConfigService;

    @Autowired
    public ProtocolClient(TInfoNetworkConfigService infoNetworkConfigService) {
        this.infoNetworkConfigService = infoNetworkConfigService;
    }
    private static final Logger log = LoggerFactory.getLogger(ProtocolClient.class);

    // 协议帧固定参数（根据你的协议修改）
    private static final byte[] FRAME_HEADER = {(byte) 0xA8, (byte) 0xB6, (byte) 0xC4, (byte) 0xD2}; // 帧头
    private static final byte[] RESERVED = {0x00, 0x00}; // 保留字段
    private static final byte OPCODE = (byte) 0xE1; // 操作码（如“自动发布”对应的值）

    // CRC 算法核心参数（根据你的协议修改）
    private static final int CRC_POLYNOMIAL = 0x8005; // 多项式
    private static final int CRC_INIT = 0xFFFF; // 初始值
    @Resource
    private QianFengPlayLedService qianFengPlayLedService;
    public void sendAutoPublishData(TInfoGuidePublish task) { // 假设 Task 是你的任务类
        try {
            if (infoNetworkConfigService == null) {
                log.error("TInfoNetworkConfigService 未正确注入");
                throw new RuntimeException("网络配置服务未初始化");
            }
            // 1. 查询并配置上位机网络参数（你的原始代码）
            TInfoNetworkConfig networkConfig = infoNetworkConfigService.selectByGuideType(1);
            if (networkConfig == null) {
                throw new RuntimeException("未查询到网络配置信息");
            }
            JSONArray originalPublishDetail = JSONArray.parseArray(task.getPublishDetail());

            // 如果元素是 JSONObject，可强转后操作
            log.error("本次自动发布总数111：{}", originalPublishDetail.size());
            for (int i = 0; i < originalPublishDetail.size(); i++) {
                Object item = originalPublishDetail.get(i);
                log.error("自动发布[{}]：{}", i, item);
            try{
                // 如果元素是 JSONObject，可强转后操作
                if (item instanceof JSONObject) {
                    JSONObject obj = (JSONObject) item;
                    String ip = obj.getString("ip"); // 获取ip字段值

//                 switch (ip) {
//                     case "192.168.1.14":
//                         log.error("192.168.1.14ip：{}", ip);
//                         break;
//                     case "172.20.157.132":
//                         log.error("172.20.157.132ip：{}", ip);
//                         break;
//                         case "172.20.151.130":
//                         log.error("172.20.151.130ip：{}", ip);
//                         break;
//                         case "172.20.19.134":
//                         log.error("172.20.19.134ip：{}", ip);
//                         break;
//                         case"172.20.209.131":
//                         log.error("172.20.209.131ip：{}", ip);
//                         break;
//                         case"172.20.209.132":
//                         log.error("172.20.209.132ip：{}", ip);
//                         break;
//                         case"172.20.209.135":
//                         log.error("172.20.209.135ip：{}", ip);
//                         break;
//                     default:
//                         continue;
//                 }
                    int string = obj.getIntValue("prgid");
                    // 覆盖IP
                    networkConfig.setIp(ip);
                    networkConfig.setPort(5225); // 覆盖端口

                    // 2. 组装业务参数（你的原始代码）
                    JSONObject param = new JSONObject();
                    param.put("user", networkConfig.getUsername());
                    param.put("pwd", networkConfig.getPassword());
                    param.put("cmd", "1"); // 命令标识
                    param.put("data", obj); // 业务数据

                    // 3. 构建协议帧（按协议格式组装）
                    byte[] businessDataBytes = param.toJSONString().getBytes(StandardCharsets.UTF_8); // 业务数据转字节
                    log.error("业务数据：{}", businessDataBytes);
                    int frameNo = 0; // 帧序号（实际可能从0递增）
                    int totalDataLen = businessDataBytes.length; // 数据总长
                    int frameDataLen = businessDataBytes.length; // 当前帧数据长度（不分包时等于总长）

                    byte address = 0x01; // 设备地址（根据实际填写）

                    // 3.1 拼接帧头到业务数据的字节（用于计算CRC）
                    ByteBuffer frameBuffer = ByteBuffer.allocate(22);
                    //ByteBuffer frameBuffer = ByteBuffer.allocate(0);
                    frameBuffer.put(FRAME_HEADER); // 帧头（4字节）
                    frameBuffer.put(address); // 地址（1字节）
                    frameBuffer.put(RESERVED); // 保留字段（2字节）
                    frameBuffer.put(OPCODE); // 操作码（1字节）
                    frameBuffer.order(ByteOrder.LITTLE_ENDIAN); // 帧序号（4字节，大端序）
                    frameBuffer.putInt(frameNo);
                    frameBuffer.putInt(2); // 数据总长（4字节）
                    frameBuffer.putInt(2); // 数据帧长（4字节）
                    frameBuffer.putShort((short) string); // 业务数据（N字节）
                    byte[] dataBeforeCRC = frameBuffer.array(); // CRC计算范围：帧头→业务数据
                    // 4. 计算CRC校验码（使用协议专属算法）
                    // 将dataBeforeCRC改成int[]
                    int[] dataBeforeCRCInt = new int[dataBeforeCRC.length];
                    for (int j = 0; j < dataBeforeCRC.length; j++) {
                        dataBeforeCRCInt[j] = dataBeforeCRC[j] & 0xFF;
                        //log.error("dataBeforeCRCInt：{}", dataBeforeCRCInt);
                    }
                    short crcValue = (short) CRCUtils.crc16(dataBeforeCRCInt);           //(short) calculateProtocolCRC(dataBeforeCRC);

                    //byte[] crcBytes = crcToBytes(crcValue); // CRC转2字节数组
                    //log.error("crcBytes：{}", crcBytes);
                    // 5. 组装完整帧（数据 + CRC）
                    ByteBuffer fullFrameBuffer = ByteBuffer.allocate(dataBeforeCRC.length + 2);
                    fullFrameBuffer.order(ByteOrder.LITTLE_ENDIAN);
                    fullFrameBuffer.put(dataBeforeCRC);
                    fullFrameBuffer.putShort(crcValue);
                    byte[] fullFrame =    fullFrame = fullFrameBuffer.array(); // 最终发送的二进制帧
                    // 转换为十六进制字符串
                    StringBuilder hexString = new StringBuilder();
                    for (byte b : fullFrame) {
                        hexString.append(String.format("0X%02X ", b));
                    }

                    log.error("待发送 fullFrame十六进制：{}", hexString.toString().trim());
                    int businessDataStart = 4 + 1 + 2 + 1 + 4 + 4 + 4;
// 业务数据长度：从代码看是 short 类型（2字节），也可通过“数据帧长”字段动态获取
                    int businessDataLength = 2; // 或从 frameBuffer 中解析的“数据帧长”（这里是 2）

// 校验业务数据范围是否合法
//                    if (fullFrame.length >= businessDataStart + businessDataLength) {
//                        // 截取纯业务数据
//                        byte[] businessData = new byte[businessDataLength];
//                        System.arraycopy(fullFrame, businessDataStart, businessData, 0, businessDataLength);
//
//                        // 打印纯业务数据（原始字节数组）
//                        log.error("纯业务数据（字节数组）：{}", Arrays.toString(businessData));
//
//                        // 转换为十六进制打印（更直观）
//                        StringBuilder businessHex = new StringBuilder();
//                        for (byte b : businessData) {
//                            businessHex.append(String.format("0X%02X ", b));
//                        }
//                        log.error("纯业务数据（十六进制）：{}", businessHex.toString().trim());
//
//                        // 如果业务数据是字符串（假设是 ASCII 或 UTF-8 编码），可尝试转换为字符串
//                        try {
//                            String businessStr = new String(businessData, StandardCharsets.UTF_8);
//                            log.error("纯业务数据（字符串）：{}", businessStr);
//                        } catch (Exception e) {
//                            log.error("业务数据转换字符串失败", e);
//                        }
//                    } else {
//                        log.error("帧数据长度不足，无法提取业务数据");
//                    }
                    log.error("目标信息 ip[{}] 节目[{}]" + ip, string );
                    sendFrame(networkConfig.getIp(), networkConfig.getPort(), fullFrame);

                }
                       } catch (Exception e) {
                    log.error("处理第{}个元素时出错", i, e); // 记录异常，但不中断循环
                    continue;
                    }
            }
        } catch (Exception e) {
            log.error("自动发布失败", e);

        }
    }

    public void sendAutoPublishData_Readback(TInfoGuidePublish task) { // 假设 Task 是你的任务类
        try {
            if (infoNetworkConfigService == null) {
                log.error("TInfoNetworkConfigService 未正确注入");
                throw new RuntimeException("网络配置服务未初始化");
            }
            // 1. 查询并配置上位机网络参数（你的原始代码）
            TInfoNetworkConfig networkConfig = infoNetworkConfigService.selectByGuideType(1);
            if (networkConfig == null) {
                throw new RuntimeException("未查询到网络配置信息");
            }
            JSONArray originalPublishDetail = JSONArray.parseArray(task.getPublishDetail());

            log.error("自动发布：{}", networkConfig);
            for (int i = 0; i < originalPublishDetail.size(); i++) {
                Object item = originalPublishDetail.get(i);
                try{
                    // 如果元素是 JSONObject，可强转后操作
                    if (item instanceof JSONObject) {
                        JSONObject obj = (JSONObject) item;
                        String ip = obj.getString("ip"); // 获取ip字段值

//                switch (ip) {
//                    case "172.20.157.133":
//                        log.error("ip"+ ip);
//                        break;
//                    case "172.20.157.132":
//                        log.error("ip"+ ip);
//                        break;
//                    default:
//                        continue;
//                }
                        int string = obj.getIntValue("prgid");
                        networkConfig.setIp(ip); // 覆盖IP

                        networkConfig.setPort(5225); // 覆盖端口

                        // 2. 组装业务参数（你的原始代码）
                        JSONObject param = new JSONObject();
                        param.put("user", networkConfig.getUsername());
                        param.put("pwd", networkConfig.getPassword());
                        param.put("cmd", "1"); // 命令标识
                        param.put("data", originalPublishDetail); // 业务数据

                        // 3. 构建协议帧（按协议格式组装）
                        byte[] businessDataBytes = param.toJSONString().getBytes(StandardCharsets.UTF_8); // 业务数据转字节
                        log.error("业务数据：{}", businessDataBytes);
                        int frameNo = 1; // 帧序号（实际可能从0递增）
                        int totalDataLen = businessDataBytes.length; // 数据总长
                        int frameDataLen = businessDataBytes.length; // 当前帧数据长度（不分包时等于总长）

                        byte address = 0x01; // 设备地址（根据实际填写）

                        // 3.1 拼接帧头到业务数据的字节（用于计算CRC）
                        ByteBuffer frameBuffer = ByteBuffer.allocate(22 + 6);
                        //ByteBuffer frameBuffer = ByteBuffer.allocate(0);
                        frameBuffer.put(FRAME_HEADER); // 帧头（4字节）
                        frameBuffer.put(address); // 地址（1字节）
                        frameBuffer.put(RESERVED); // 保留字段（2字节）
                        frameBuffer.put(OPCODE); // 操作码（1字节）
                        frameBuffer.order(ByteOrder.LITTLE_ENDIAN); // 帧序号（4字节，大端序）
                        frameBuffer.putInt(frameNo);
                        frameBuffer.putInt(8); // 数据总长（4字节）
                        frameBuffer.putInt(8); // 数据帧长（4字节）
                        frameBuffer.putShort((short) string); // 业务数据（N字节）
                        byte[] dataBeforeCRC = frameBuffer.array(); // CRC计算范围：帧头→业务数据
                        log.error("dataBeforeCRC：{}", dataBeforeCRC);
                        // 4. 计算CRC校验码（使用协议专属算法）
                        // 将dataBeforeCRC改成int[]
                        int[] dataBeforeCRCInt = new int[dataBeforeCRC.length];
                        for (int j = 0; j < dataBeforeCRC.length; j++) {
                            dataBeforeCRCInt[j] = dataBeforeCRC[j] & 0xFF;
                            //log.error("dataBeforeCRCInt：{}", dataBeforeCRCInt);
                        }
                        short crcValue = (short) CRCUtils.crc16(dataBeforeCRCInt);           //(short) calculateProtocolCRC(dataBeforeCRC);

                        //byte[] crcBytes = crcToBytes(crcValue); // CRC转2字节数组
                        //log.error("crcBytes：{}", crcBytes);
                        // 5. 组装完整帧（数据 + CRC）
                        ByteBuffer fullFrameBuffer = ByteBuffer.allocate(dataBeforeCRC.length + 2);
                        fullFrameBuffer.order(ByteOrder.LITTLE_ENDIAN);
                        fullFrameBuffer.put(dataBeforeCRC);
                        fullFrameBuffer.putShort(crcValue);
                        byte[] fullFrame = fullFrameBuffer.array(); // 最终发送的二进制帧
                        // 转换为十六进制字符串
                        StringBuilder hexString = new StringBuilder();
                        for (byte b : fullFrame) {
                            hexString.append(String.format("0X%02X ", b));
                        }

                        log.error("fullFrame：{}", fullFrame);
                        log.error("fullFrame十六进制：{}", hexString.toString().trim());
                        int businessDataStart = 4 + 1 + 2 + 1 + 4 + 4 + 4;
// 业务数据长度：从代码看是 short 类型（2字节），也可通过“数据帧长”字段动态获取
                        int businessDataLength = 2; // 或从 frameBuffer 中解析的“数据帧长”（这里是 2）

// 校验业务数据范围是否合法
                        if (fullFrame.length >= businessDataStart + businessDataLength) {
                            // 截取纯业务数据
                            byte[] businessData = new byte[businessDataLength];
                            System.arraycopy(fullFrame, businessDataStart, businessData, 0, businessDataLength);

                            // 打印纯业务数据（原始字节数组）
                            log.error("纯业务数据（字节数组）：{}", Arrays.toString(businessData));

                            // 转换为十六进制打印（更直观）
                            StringBuilder businessHex = new StringBuilder();
                            for (byte b : businessData) {
                                businessHex.append(String.format("0X%02X ", b));
                            }
                            log.error("纯业务数据（十六进制）：{}", businessHex.toString().trim());

                            // 如果业务数据是字符串（假设是 ASCII 或 UTF-8 编码），可尝试转换为字符串
                            try {
                                String businessStr = new String(businessData, StandardCharsets.UTF_8);
                                log.error("纯业务数据（字符串）：{}", businessStr);
                            } catch (Exception e) {
                                log.error("业务数据转换字符串失败", e);
                            }
                        } else {
                            log.error("帧数据长度不足，无法提取业务数据");
                        }
                        sendFrame(networkConfig.getIp(), networkConfig.getPort(), fullFrame);
                    }
                } catch (Exception e) {
                    log.error("处理第{}个元素时出错", i, e); // 记录异常，但不中断循环
                    continue;
                }
            }
        } catch (Exception e) {
            log.error("自动发布失败", e);
        }
    }
    // 协议专属CRC16算法（根据你的协议参数实现）
    private int calculateProtocolCRC(byte[] data) {
        int crc = CRC_INIT;
        for (byte b : data) {
            crc ^= (b & 0xFF); // 与当前字节异或
            for (int i = 0; i < 8; i++) {
                if ((crc & 0x0001) != 0) { // 最低位为1
                    crc = (crc >> 1) ^ CRC_POLYNOMIAL;
                } else {
                    crc >>= 1;
                }
            }
        }
        // 协议要求：高低位交换（根据实际协议决定是否需要）
        crc = ((crc & 0xFF00) >> 8) | ((crc & 0x00FF) << 8);
        return crc & 0xFFFF;
    }

    // CRC值转2字节数组（大端序）
    private byte[] crcToBytes(int crc) {
        return new byte[]{(byte) (crc >> 8), (byte) crc};
    }

    // 发送帧到上位机（简化示例）
    private void sendFrame(String ip, int port, byte[] frame) throws Exception {
        // 实际开发中使用Socket/TCP发送二进制数据
        log.info("发送帧到 {}:{}，长度：{}字节", ip, port, frame.length);
        // 示例：Socket socket = new Socket(ip, port);
        // OutputStream out = socket.getOutputStream();
        // out.write(frame);
      //log.error("ipip", ip);
        byte[] s = qianFengPlayLedService.controlLeds(
                ip,
                port,
                frame
        );

        if (s != null) {
            StringBuilder businessHex = new StringBuilder();
            for (byte b : s) {
                businessHex.append(String.format("0X%02X ", b));
            }
            log.error("返回帧 数据（十六进制）：{}", businessHex.toString().trim());
        }

        int businessDataStart = 4 + 1 + 2 + 1 + 4 + 4 + 4;
// 业务数据长度：从代码看是 short 类型（2字节），也可通过“数据帧长”字段动态获取
        int businessDataLength = 2; // 或从 frameBuffer 中解析的“数据帧长”（这里是 2）

// 校验业务数据范围是否合法
        if (s.length >= businessDataStart + businessDataLength) {
//            // 截取纯业务数据
//            byte[] businessData = new byte[businessDataLength];
//            System.arraycopy(s, businessDataStart, businessData, 0, businessDataLength);

            // 打印纯业务数据（原始字节数组）
            //log.error("纯业务数据（字节数组）：{}", Arrays.toString(businessData));

            // 转换为十六进制打印（更直观）
//            StringBuilder businessHex = new StringBuilder();
//            for (byte b : businessData) {
//                businessHex.append(String.format("0X%02X ", b));
//            }
            //log.error("纯业务数据（十六进制）：{}", businessHex.toString().trim());

            // 如果业务数据是字符串（假设是 ASCII 或 UTF-8 编码），可尝试转换为字符串
//            try {
//                String businessStr = new String(businessData, StandardCharsets.UTF_8);
//                //log.error("纯业务数据（字符串）：{}", businessStr);
//            } catch (Exception e) {
//                log.error("业务数据转换字符串失败", e);
//            }
        } else {
            log.error("返回帧数据长度不足，无法提取业务数据");
        }
        //log.info("返回结果：{}", s);

// 解析错误码（根据协议，错误码为4字节，低字节先，对应数据段的D0-D3）
        if (s != null && s.length >= 24) { // 确保数据长度足够解析错误码（帧头4 + 其他字段 + 错误码4 + CRC2）
            // 错误码位于第21-24字节（索引20-23），低字节先，需组合成int
            //从20开始取四个
            int errorCode = ((s[23] & 0xFF) << 24) | ((s[22] & 0xFF) << 16) | ((s[21] & 0xFF) << 8) | (s[20] & 0xFF);
            StringBuilder hexLog = new StringBuilder();
//            for (byte b : s) {
//                hexLog.append(String.format("0x%02X ", b));
//            }
//            log.error("完整应答帧（十六进制）：{}", hexLog.toString().trim());
            switch (errorCode) {
                case 0x00:
                    log.error("返回帧数据判断 请求帧接收成功，但数据还未接收完毕");
                    break;
                case 0x01:
                    log.error("返回帧数据判断 显示数据下载完成");
                    break;
                default:
                    log.error("返回帧数据判断 操作出错，错误码：0x{0:04X}", errorCode);
            }
        } else {
            log.error("应答帧数据异常，长度不足或为null，无法解析错误码");
        }
    }

    // 内部任务类（仅作示例）
    static class Task {
        private String publishDetail;
        public String getPublishDetail() { return publishDetail; }
        public void setPublishDetail(String publishDetail) { this.publishDetail = publishDetail; }
    }
}


 class CRCUtils {

    /**
     * 一个字节占 8位
     */
    private static final int BITS_OF_BYTE = 8;

    /**
     * 多项式
     */
    private static final int POLYNOMIAL = 0XA001;

    /**
     * CRC寄存器默认初始值
     */
    private static final int INITIAL_VALUE = 0XFFFF;

    /**
     * CRC16 编码
     * @param bytes 编码内容
     * @return 编码结果
     */
    public static int crc16(int[] bytes) {
        int res = INITIAL_VALUE;
        for (int data : bytes) {
            res = res ^ data;
            for (int i = 0; i < BITS_OF_BYTE; i++) {
                res = (res & 0X0001) == 1 ? (res >> 1) ^ POLYNOMIAL : res >> 1;
            }
        }
        return res;
    }
}